{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "0513.ipynb",
      "provenance": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/yananma/5_programs_per_day/blob/master/0513.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yM2nIgQoSbZN",
        "colab_type": "text"
      },
      "source": [
        "## 9.13 实战 Kaggle 比赛：狗的品种识别 ( ImageNet Dogs )"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "e4UKeuIJJ-YE",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "!pip install mxnet d2lzh"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "f4tJoP82KbD6",
        "colab_type": "code",
        "outputId": "ff2847ba-48d1-480c-b230-24a72c90cb05",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 151
        }
      },
      "source": [
        "!git clone https://www.github.com/d2l-ai/d2l-zh.git"
      ],
      "execution_count": 2,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Cloning into 'd2l-zh'...\n",
            "warning: redirecting to https://github.com/d2l-ai/d2l-zh.git/\n",
            "remote: Enumerating objects: 17, done.\u001b[K\n",
            "remote: Counting objects:   5% (1/17)\u001b[K\rremote: Counting objects:  11% (2/17)\u001b[K\rremote: Counting objects:  17% (3/17)\u001b[K\rremote: Counting objects:  23% (4/17)\u001b[K\rremote: Counting objects:  29% (5/17)\u001b[K\rremote: Counting objects:  35% (6/17)\u001b[K\rremote: Counting objects:  41% (7/17)\u001b[K\rremote: Counting objects:  47% (8/17)\u001b[K\rremote: Counting objects:  52% (9/17)\u001b[K\rremote: Counting objects:  58% (10/17)\u001b[K\rremote: Counting objects:  64% (11/17)\u001b[K\rremote: Counting objects:  70% (12/17)\u001b[K\rremote: Counting objects:  76% (13/17)\u001b[K\rremote: Counting objects:  82% (14/17)\u001b[K\rremote: Counting objects:  88% (15/17)\u001b[K\rremote: Counting objects:  94% (16/17)\u001b[K\rremote: Counting objects: 100% (17/17)\u001b[K\rremote: Counting objects: 100% (17/17), done.\u001b[K\n",
            "remote: Compressing objects: 100% (13/13), done.\u001b[K\n",
            "remote: Total 15702 (delta 9), reused 8 (delta 4), pack-reused 15685\u001b[K\n",
            "Receiving objects: 100% (15702/15702), 159.56 MiB | 43.06 MiB/s, done.\n",
            "Resolving deltas: 100% (11132/11132), done.\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-qqf9DYpKs2a",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "!mkdir ../data"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "K8AEhBD6LS2p",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "!cp -r ./d2l-zh/data/kaggle_dog/ ../data/"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "5adwmleDLf6Z",
        "colab_type": "code",
        "outputId": "a0906b8e-8518-4d05-ee4c-55499d3684ba",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "!ls ../data/kaggle_dog/"
      ],
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "train_valid_test_tiny.zip\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "peIHYbiqLlU2",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "import collections \n",
        "import d2lzh as d2l \n",
        "import math \n",
        "from mxnet import autograd, gluon, init, nd \n",
        "from mxnet.gluon import data as gdata, loss as gloss, model_zoo, nn \n",
        "import os \n",
        "import shutil \n",
        "import time \n",
        "import zipfile "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HHKeJiZjM9v_",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "demo = True \n",
        "data_dir = \"../data/kaggle_dog\"\n",
        "if demo:\n",
        "    zipfiles = ['train_valid_test_tiny.zip']\n",
        "else:\n",
        "    zipfiles = ['train.zip', 'test.zip', 'lables.csv.zip']\n",
        "for f in zipfiles:\n",
        "    with zipfile.ZipFile(data_dir + '/' + f, 'r') as z:\n",
        "        z.extractall(data_dir)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "60g_zpbXOHd7",
        "colab_type": "code",
        "outputId": "a9a07fda-59d2-4c50-c509-a9e33f492c2e",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "!ls ../data/kaggle_dog/"
      ],
      "execution_count": 8,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "train_valid_test_tiny  train_valid_test_tiny.zip\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HLj1ns-dOL2V",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def reorg_train_valid(data_dir, train_dir, input_dir, valid_ratio, idx_label):\n",
        "    min_n_train_per_label = (collections.Counter(idx_label()).most_common()[:-2:-1][0][1])\n",
        "    n_valid_per_label = math.floor(min_n_train_per_label * valid_ratio)\n",
        "    label_count = {}\n",
        "    for train_file in os.listdir(os.path.join(data_dir, train_dir)):\n",
        "        idx = train_file.split('.')[0]\n",
        "        label = idx_label[idx]\n",
        "        d2l.mkdir_if_not_exist([data_dir, input_dir, 'train_valid', label])\n",
        "        shutil.copy(os.path.join(data_dir, train_dir, train_file), \n",
        "              os.path.join(data_dir, input_dir, 'train_valid', label))\n",
        "        if label not in label_count or label_count[label] < n_valid_per_label:\n",
        "            d2l.mkdir_if_not_exist([data_dir, input_dir, 'valid', label])\n",
        "            shutil.copy(os.path.join(data_dir, train_dir, train_file), \n",
        "                  (os.path.join(data_dir, input_dir, 'valid', label)))\n",
        "            label_count[label] = label_count.get(label, 0) + 1 \n",
        "        else:\n",
        "            d2l.mkdir_if_not_exist([data_dir, input_dir, 'train', label])\n",
        "            shutil.copy(os.path.join(data_dir, train_dir, train_file), \n",
        "                  (os.path.join(data_dir, input_dir, 'train', label)))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cgWhWZP1Rz4n",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def reorg_dog_data(data_dir, label_file, train_dir, test_dir, input_dir, valid_ratio):\n",
        "    with open(os.path.join(data_dir, label_file), 'r') as f:\n",
        "        lines = f.readlines()[1:]\n",
        "        tokens = [l.rstrip().split(',') for l in lines]\n",
        "        idx_label = dict(((idx, label) for idx, label in tokens))\n",
        "    reorg_train_valid(data_dir, train_dir, input_dir, valid_ratio, idx_label)\n",
        "    d2l.mkdir_if_not_exist([data_dir, input_dir, 'test', 'unknown'])\n",
        "    for test_file in os.listdir(os.path.join(data_dir, test_dir)):\n",
        "        shutil.copy(os.path.join(data_dir, test_dir, test_file), \n",
        "              os.path.join(data_dir, input_dir, 'test', 'unknown'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HixX5x1Rlbru",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "if demo:\n",
        "    input_dir, batch_size = 'train_valid_test_tiny', 1 \n",
        "else:\n",
        "    label_file, train_dir, test_dir = 'label.csv', 'train', 'test'\n",
        "    input_dir, batch_size, valid_ratio = 'train_valid_test', 128, 0.1\n",
        "    reorg_dog_data(data_dir, label_file, train_dir, test_dir, input_dir, valid_ratio)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "iR5ERzN7mXoS",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "transform_train = gdata.vision.transforms.Compose([\n",
        "    gdata.vision.transforms.RandomResizedCrop(224, scale=(0.08, 1.0), ratio=(3.0/4.0, 4.0/3.0)), \n",
        "    gdata.vision.transforms.RandomFlipLeftRight(), \n",
        "    gdata.vision.transforms.RandomColorJitter(brightness=0.4, contrast=0.4, saturation=0.4), \n",
        "    gdata.vision.transforms.RandomLighting(0.1), \n",
        "    gdata.vision.transforms.ToTensor(), \n",
        "    gdata.vision.transforms.Normalize([0.485, 0.456, 0.406], \n",
        "                      [0.229, 0.224, 0.225])\n",
        "])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "onz4uysooTC7",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "transform_test = gdata.vision.transforms.Compose([\n",
        "    gdata.vision.transforms.Resize(256), \n",
        "    gdata.vision.transforms.CenterCrop(224), \n",
        "    gdata.vision.transforms.ToTensor(), \n",
        "    gdata.vision.transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])\n",
        "])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "H20eQCyfpJ16",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "train_ds = gdata.vision.ImageFolderDataset(os.path.join(data_dir, input_dir, 'train'), flag=1)\n",
        "valid_ds = gdata.vision.ImageFolderDataset(os.path.join(data_dir, input_dir, 'valid'), flag=1)\n",
        "train_valid_ds = gdata.vision.ImageFolderDataset(os.path.join(data_dir, input_dir, 'train_valid'), flag=1)\n",
        "test_ds = gdata.vision.ImageFolderDataset(os.path.join(data_dir, input_dir, 'test'), flag=1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "b787QKEMzEIP",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "train_iter = gdata.DataLoader(train_ds.transform_first(transform_train), batch_size, shuffle=True, last_batch='keep')\n",
        "valid_iter = gdata.DataLoader(valid_ds.transform_first(transform_test), batch_size, shuffle=True, last_batch='keep')\n",
        "train_valid_iter = gdata.DataLoader(train_valid_ds.transform_first(transform_train), batch_size, shuffle=True, last_batch='keep')\n",
        "test_iter = gdata.DataLoader(test_ds.transform_first(transform_test), batch_size, shuffle=False, last_batch='keep')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "4HHTa5h_0Kt5",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def get_net(ctx):\n",
        "    finetune_net = model_zoo.vision.resnet34_v2(pretrained=True)\n",
        "    finetune_net.output_new = nn.HybridSequential(prefix='')\n",
        "    finetune_net.output_new.add(nn.Dense(256, activation='relu'))\n",
        "    finetune_net.output_new.add(nn.Dense(120))\n",
        "    finetune_net.output_new.initialize(init.Xavier(), ctx=ctx)\n",
        "    finetune_net.collect_params().reset_ctx(ctx)\n",
        "    return finetune_net"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "D6EUG8Z95sxQ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "loss = gloss.SoftmaxCrossEntropyLoss()\n",
        "\n",
        "def evaluate_loss(data_iter, net, ctx):\n",
        "    l_sum, n = 0.0, 0 \n",
        "    for X, y in data_iter:\n",
        "        y = y.as_in_context(ctx)\n",
        "        output_features = net.features(X.as_in_context(ctx))\n",
        "        outputs = net.output_new(output_features)\n",
        "        l_sum += loss(outputs, y).sum().asscalar()\n",
        "        n += y.size \n",
        "    return l_sum / n "
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "-XB8fpZE6aX3",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "def train(net, train_iter, valid_iter, num_epochs, lr, wd, ctx, lr_period, lr_decay):\n",
        "    trainer = gluon.Trainer(net.output_new.collect_params(), 'sgd', {'learning_rate': lr, 'momentum': 0.9, 'wd':wd})\n",
        "    for epoch in range(num_epochs):\n",
        "        train_l_sum, n, start = 0.0, 0, time.time()\n",
        "        if epoch > 0 and epoch % lr_period == 0:\n",
        "            trainer.set_learning_rate(trainer.learning_rate * lr_decay)\n",
        "        for X, y in train_iter:\n",
        "            y = y.as_in_context(ctx)\n",
        "            output_features = net.features(X.as_in_context(ctx))\n",
        "            with autograd.record():\n",
        "                outputs = net.output_new(output_features)\n",
        "                l = loss(outputs, y).sum()\n",
        "            l.backward()\n",
        "            trainer.step(batch_size)\n",
        "            train_l_sum += l.asscalar()\n",
        "            n += y.size \n",
        "        time_s = \"time %.2f sec\" % (time.time() - start)\n",
        "        if valid_iter is not None:\n",
        "            valid_loss = evaluate_loss(valid_iter, net, ctx) \n",
        "            epoch_s = (\"epoch %d, train loss %f, valid loss %f, \" \n",
        "                    % (epoch + 1, train_l_sum / n, valid_loss))\n",
        "        else:\n",
        "            epoch_s = ('epoch %d, train loss %f, '\n",
        "                  % (epoch + 1, train_l_sum / n))\n",
        "        print(epoch_s + time_s + ', lr ' + str(trainer.learning_rate))\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XsXSF26NBtWg",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "45ad12ba-24c0-4377-8ade-0c81fae86687"
      },
      "source": [
        "ctx, num_epochs, lr, wd = d2l.try_gpu(), 1, 0.01, 1e-4 \n",
        "lr_period, lr_decay, net = 10, 0.1, get_net(ctx)\n",
        "net.hybridize()\n",
        "train(net, train_iter, valid_iter, num_epochs, lr, wd, ctx, lr_period, lr_decay)"
      ],
      "execution_count": 24,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "epoch 1, train loss 5.270478, valid loss 4.732494, time 25.40 sec, lr 0.01\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VcCWyB6sDAt8",
        "colab_type": "code",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        },
        "outputId": "81714bbf-f074-4b93-e109-85cebb172575"
      },
      "source": [
        "net = get_net(ctx)\n",
        "net.hybridize()\n",
        "train(net, train_valid_iter, None, num_epochs, lr, wd, ctx, lr_period, lr_decay)\n",
        "\n",
        "preds = []\n",
        "for data, label in test_iter:\n",
        "    output_features = net.features(data.as_in_context(ctx))\n",
        "    output = nd.softmax(net.output_new(output_features))\n",
        "    preds.extend(output.asnumpy())\n",
        "ids = sorted(os.listdir(os.path.join(data_dir, input_dir, 'test/unknown')))\n",
        "with open('submission.csv', 'w') as f:\n",
        "    f.write('id,' + ','.join(train_valid_ds.synsets) + '\\n')\n",
        "    for i, output in zip(ids, preds):\n",
        "        f.write(i.split('.')[0] + ',' + ','.join([str(num) for num in output]) + '\\n')\n"
      ],
      "execution_count": 25,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "epoch 1, train loss 5.070011, time 51.34 sec, lr 0.01\n"
          ],
          "name": "stdout"
        }
      ]
    }
  ]
}